﻿///////////////////////////////////////////////////////////////////////////////
//
// This file is part of the BassDrop project
//   https://code.google.com/p/bass-drop/
//
//
// Creator:	Gergely Krajcsovszki (krajcsovszkig@hotmail.com)
// Date:	2014-04-20 11:30
//
//
// Copyright © 2013-2014. Gergely Krajcsovszki
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BassDrop.Core.Common.Data.IO.Data
{
	/// <summary>
	/// Class containing information that can be used to locate a resource (eg. media file)
	/// </summary>
	public class ResourceLocation
	{
		/// <summary>
		/// The list of possible base paths for relative paths.
		/// The Uris are absolute.
		/// </summary>
		public HashSet<ResourceLocationInfoBasePath> BasePaths
		{
			get;
			private set;
		}

		/// <summary>
		/// A non-explicitly stated base path for relative paths,
		/// usually the working directory of the application.
		/// The Uri is absolute.
		/// </summary>
		public Uri ImplicitBasePath
		{
			get;
			private set;
		}

		/// <summary>
		/// The base path for local paths, eg. the directory of the playlist
		/// file that contains this media file.
		/// The Uri is absolute.
		/// </summary>
		public Uri LocalBasePath
		{
			get;
			private set;
		}

		/// <summary>
		/// The key identifying the resource, eg. the contents of the key tag in an xplf file
		/// </summary>
		public MediaKey Key
		{
			get;
			private set;
		}

		/// <summary>
		/// The metadata identifying the resource, eg. the contents of the meta tag in an xplf file
		/// </summary>
		public MediaKey Meta
		{
			get;
			private set;
		}

		/// <summary>
		/// The IDs of this resource on different systems
		/// </summary>
		public HashSet<ResourceLocationInfoId> Ids
		{
			get;
			private set;
		}

		/// <summary>
		/// The relative paths to this resource on different systems
		/// </summary>
		public HashSet<ResourceLocationInfoRelativePath> RelativePaths
		{
			get;
			private set;
		}

		/// <summary>
		/// The absolute paths to this resource on different systems
		/// </summary>
		public HashSet<ResourceLocationInfoAbsolutePath> AbsolutePaths
		{
			get;
			private set;
		}

		/// <summary>
		/// The local paths to this resource on different systems
		/// </summary>
		public HashSet<ResourceLocationInfoLocalPath> LocalPaths
		{
			get;
			private set;
		}

		/// <summary>
		/// Constructs a new ResourceLocation
		/// </summary>
		/// <param name="implicitBasePath">
		///		A non-explicitly stated base path for relative paths,
		///		usually the working directory of the application.
		///		The Uri is absolute.
		/// </param>
		/// <param name="localBasePath">
		///		The base path for local paths, eg. the directory of the playlist
		///		file that contains this media file.
		///		The Uri is absolute.
		/// </param>
		public ResourceLocation( Uri implicitBasePath, Uri localBasePath )
		{
			ImplicitBasePath = implicitBasePath;
			LocalBasePath = localBasePath;
			BasePaths = new HashSet<ResourceLocationInfoBasePath>();

			Ids = new HashSet<ResourceLocationInfoId>();
			RelativePaths = new HashSet<ResourceLocationInfoRelativePath>();
			AbsolutePaths = new HashSet<ResourceLocationInfoAbsolutePath>();
			LocalPaths = new HashSet<ResourceLocationInfoLocalPath>();

			Key = new MediaKey();
			Meta = new MediaKey();
		}

		/// <summary>
		/// Returns the list of possible IDs for this item.
		/// </summary>
		/// <param name="client">If a non-null value is passed, will only return items compatible with this type of application</param>
		/// <param name="location">If a non-null value is passed, will only return items compatible with this location / machine</param>
		/// <returns></returns>
		public IEnumerable<string> GetPossibleIds( string client = null, string location = null )
		{
			foreach ( var id in Ids )
			{
				if ( id.DoesApplyFor( client, location ) )
				{
					yield return id.Id;
				}
			}
		}

		/// <summary>
		/// Returns the list of possible absolute URIs for this item. Does not use the list of IDs
		/// and may return a path multiple times.
		/// First the relative paths are processed, then the absolute paths, last the local paths.
		/// </summary>
		/// <param name="client">If a non-null value is passed, will only return items compatible with this type of application</param>
		/// <param name="location">If a non-null value is passed, will only return items compatible with this location / machine</param>
		/// <returns></returns>
		public IEnumerable<Uri> GetPossibleAbsoluteUris( string client = null, string location = null )
		{
			foreach ( var relativePath in RelativePaths )
			{
				if ( relativePath.DoesApplyFor( client, location ) )
				{
					foreach ( var absoluteUri in relativePath.GetPossibleAbsoluteUris( client, location ) )
					{
						yield return absoluteUri;
					}
				}
			}

			foreach ( var absolutePath in AbsolutePaths )
			{
				if ( absolutePath.DoesApplyFor( client, location ) )
				{
					yield return absolutePath.AbsolutePath;
				}
			}

			foreach ( var localPath in LocalPaths )
			{
				if ( localPath.DoesApplyFor( client, location ) )
				{
					yield return localPath.GetAbsoluteUri();
				}
			}
		}

		/// <summary>
		/// Merges another resource location into this one
		/// </summary>
		/// <param name="other"></param>
		public void Merge( ResourceLocation other )
		{
			if ( Key != null )
			{
				Key.Merge( other.Key );
			}

			if ( Meta != null )
			{
				Meta.Merge( other.Meta );
			}

			foreach ( var item in other.AbsolutePaths )
			{
				var oldItem = (from i
							   in AbsolutePaths
							   where i == item
							   select i).FirstOrDefault();

				if ( oldItem != null )
				{
					oldItem.Merge( item );
				}
				else
				{
					AbsolutePaths.Add( item );
				}
			}

			foreach ( var item in other.BasePaths )
			{
				var oldItem = (from i
							   in BasePaths
							   where i == item
							   select i).FirstOrDefault();

				if ( oldItem != null )
				{
					oldItem.Merge( item );
				}
				else
				{
					BasePaths.Add( item );
				}
			}

			foreach ( var item in other.Ids )
			{
				var oldItem = (from i
							   in Ids
							   where i == item
							   select i).FirstOrDefault();

				if ( oldItem != null )
				{
					oldItem.Merge( item );
				}
				else
				{
					Ids.Add( item );
				}
			}

			foreach ( var item in other.LocalPaths )
			{
				var oldItem = (from i
							   in LocalPaths
							   where i == item
							   select i).FirstOrDefault();

				if ( oldItem != null )
				{
					oldItem.Merge( item );
				}
				else
				{
					LocalPaths.Add( item );
				}
			}

			foreach ( var item in other.RelativePaths )
			{
				var oldItem = (from i
							   in RelativePaths
							   where i == item
							   select i).FirstOrDefault();

				if ( oldItem != null )
				{
					oldItem.Merge( item );
				}
				else
				{
					RelativePaths.Add( item );
				}
			}
		}

		/// <summary>
		/// Returns whether the two resource locations likely refer to the same media item
		/// </summary>
		/// <param name="other"></param>
		/// <returns></returns>
		public bool Match( ResourceLocation other )
		{
			if ( (object) other == null )
			{
				return false;
			}

			if ( System.Object.ReferenceEquals( this, other ) )
			{
				return true;
			}

			if ( Key != null && other.Key != null && !Key.Match( other.Key ) )
			{
				return false;
			}

			if ( Meta != null && other.Meta != null && !Meta.Match( other.Meta ) )
			{
				return false;
			}

			if ( AbsolutePaths.Count == 0 && other.AbsolutePaths.Count == 0
					&& BasePaths.Count == 0 && other.BasePaths.Count == 0
					&& Ids.Count == 0 && other.Ids.Count == 0
					&& LocalPaths.Count == 0 && other.LocalPaths.Count == 0
					&& RelativePaths.Count == 0 && other.RelativePaths.Count == 0 )
			{
				return true;
			}

			foreach ( var item in other.AbsolutePaths )
			{
				var oldItem = (from i
							   in AbsolutePaths
							   where i == item
							   select i).FirstOrDefault();

				if ( oldItem != null )
				{
					return true;
				}
			}

			foreach ( var item in other.BasePaths )
			{
				var oldItem = (from i
							   in BasePaths
							   where i == item
							   select i).FirstOrDefault();

				if ( oldItem != null )
				{
					return true;
				}
			}

			foreach ( var item in other.Ids )
			{
				var oldItem = (from i
							   in Ids
							   where i == item
							   select i).FirstOrDefault();

				if ( oldItem != null )
				{
					return true;
				}
			}

			foreach ( var item in other.LocalPaths )
			{
				var oldItem = (from i
							   in LocalPaths
							   where i == item
							   select i).FirstOrDefault();

				if ( oldItem != null )
				{
					return true;
				}
			}

			foreach ( var item in other.RelativePaths )
			{
				var oldItem = (from i
							   in RelativePaths
							   where i == item
							   select i).FirstOrDefault();

				if ( oldItem != null )
				{
					return true;
				}
			}

			return false;
		}

		#region Inner classes

		/// <summary>
		/// Base class of objects that store a piece of actual resource location data
		/// </summary>
		public abstract class ResourceLocationInfoBase : CustomDataItemBase<ResourceLocation>
		{
			/// <summary>
			/// Initializes the common properties of ResourceLocationInfoBase subclasses
			/// </summary>
			/// <param name="parent">the ResourceLocationInfoBaser object containing this object</param>
			protected ResourceLocationInfoBase( ResourceLocation parent )
				: base( parent )
			{
			}
		}

		/// <summary>
		/// Resource locator that uses a unique ID (eg. a media library entry ID) for identification
		/// </summary>
		public class ResourceLocationInfoId : ResourceLocationInfoBase, IEquatable<ResourceLocationInfoId>
		{
			/// <summary>
			/// The ID of the resource
			/// </summary>
			public string Id
			{
				get;
				private set;
			}

			/// <summary>
			/// Creates a new resource locator by ID
			/// </summary>
			/// <param name="id">the ID of the resource</param>
			/// <param name="parent">the ResourceLocation object containing this object</param>
			public ResourceLocationInfoId( string id, ResourceLocation parent )
				: base( parent )
			{
				Id = id;
			}

			#region IEquatable<ResourceLocationInfoId> Members

			/// <summary>
			/// Indicates whether the current object is equal to another object of the same type.
			/// </summary>
			/// <param name="other">An object to compare with this object.</param>
			/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
			public bool Equals( ResourceLocationInfoId other )
			{
				if ( (object) other == null )
				{
					return false;
				}

				return Match( other ) && other.Id == Id;
			}

			#endregion

			/// <summary>
			/// Determines whether the specified object is equal to the current object.
			/// </summary>
			/// <param name="obj">An object to compare with this object.</param>
			/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
			public override bool Equals( object obj )
			{
				return Equals( obj as ResourceLocationInfoId );
			}

			/// <summary>
			/// Serves as the default hash function.
			/// </summary>
			/// <returns>A hash code for the current object.</returns>
			public override int GetHashCode()
			{
				unchecked
				{
					int hash = 17;

					hash = hash * 23 + Id.GetHashCode();

					return hash;
				}
			}

			/// <summary>
			/// Returns whether rl1 equals rl2
			/// </summary>
			/// <param name="rl1"></param>
			/// <param name="rl2"></param>
			/// <returns></returns>
			public static bool operator ==( ResourceLocationInfoId rl1, ResourceLocationInfoId rl2 )
			{
				if ( System.Object.ReferenceEquals( rl1, rl2 ) )
				{
					return true;
				}

				if ( ((object) rl1 == null) || ((object) rl2 == null) )
				{
					return false;
				}

				return rl1.Equals( rl2 );
			}

			/// <summary>
			/// Returns whether rl1 does not equal rl2
			/// </summary>
			/// <param name="rl1"></param>
			/// <param name="rl2"></param>
			/// <returns></returns>
			public static bool operator !=( ResourceLocationInfoId rl1, ResourceLocationInfoId rl2 )
			{
				return !(rl1 == rl2);
			}
		}

		/// <summary>
		/// Resource locator that uses a relative path string for identification.
		/// The path is relative to one of the parent's BasePaths or the parent's ImplicitBasePath.
		/// </summary>
		public class ResourceLocationInfoRelativePath : ResourceLocationInfoBase
		{
			/// <summary>
			/// The relative path.
			/// It is relative to one of the parent's BasePaths or the parent's ImplicitBasePath.
			/// </summary>
			public string RelativePath
			{
				get;
				private set;
			}

			/// <summary>
			/// Creates a new resource locator by relative path
			/// </summary>
			/// <param name="relativePath">the relative path</param>
			/// <param name="parent">the ResourceLocation object containing this object</param>
			public ResourceLocationInfoRelativePath( string relativePath, ResourceLocation parent )
				: base( parent )
			{
				RelativePath = relativePath;
			}

			/// <summary>
			/// Returns the possible absolute Uris from this relative Uri
			/// </summary>
			/// <param name="client">If a non-null value is passed, will only return items compatible with this type of application</param>
			/// <param name="location">If a non-null value is passed, will only return items compatible with this location / machine</param>
			/// <returns></returns>
			public IEnumerable<Uri> GetPossibleAbsoluteUris( string client = null, string location = null )
			{
				foreach ( var basePath in Parent.BasePaths )
				{
					if ( basePath.DoesApplyFor( client, location ) )
					{
						yield return new Uri( basePath.AbsolutePath, RelativePath );
					}
				}

				if ( Parent.ImplicitBasePath != null )
				{
					yield return new Uri( Parent.ImplicitBasePath, RelativePath );
				}
			}

			#region IEquatable<ResourceLocationInfoId> Members

			/// <summary>
			/// Indicates whether the current object is equal to another object of the same type.
			/// </summary>
			/// <param name="other">An object to compare with this object.</param>
			/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
			public bool Equals( ResourceLocationInfoRelativePath other )
			{
				if ( (object) other == null )
				{
					return false;
				}

				return Match( other ) && other.RelativePath == RelativePath;
			}

			#endregion

			/// <summary>
			/// Determines whether the specified object is equal to the current object.
			/// </summary>
			/// <param name="obj">An object to compare with this object.</param>
			/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
			public override bool Equals( object obj )
			{
				return Equals( obj as ResourceLocationInfoRelativePath );
			}

			/// <summary>
			/// Serves as the default hash function.
			/// </summary>
			/// <returns>A hash code for the current object.</returns>
			public override int GetHashCode()
			{
				unchecked
				{
					int hash = 17;

					hash = hash * 23 + RelativePath.GetHashCode();

					return hash;
				}
			}

			/// <summary>
			/// Returns whether rl1 equals rl2
			/// </summary>
			/// <param name="rl1"></param>
			/// <param name="rl2"></param>
			/// <returns></returns>
			public static bool operator ==( ResourceLocationInfoRelativePath rl1, ResourceLocationInfoRelativePath rl2 )
			{
				if ( System.Object.ReferenceEquals( rl1, rl2 ) )
				{
					return true;
				}

				if ( ((object) rl1 == null) || ((object) rl2 == null) )
				{
					return false;
				}

				return rl1.Equals( rl2 );
			}

			/// <summary>
			/// Returns whether rl1 does not equal rl2
			/// </summary>
			/// <param name="rl1"></param>
			/// <param name="rl2"></param>
			/// <returns></returns>
			public static bool operator !=( ResourceLocationInfoRelativePath rl1, ResourceLocationInfoRelativePath rl2 )
			{
				return !(rl1 == rl2);
			}
		}

		/// <summary>
		/// Base path for relative resource locators
		/// </summary>
		public class ResourceLocationInfoBasePath : ResourceLocationInfoBase
		{
			/// <summary>
			/// The absolute path.
			/// </summary>
			public Uri AbsolutePath
			{
				get;
				private set;
			}

			/// <summary>
			/// Creates a new base path for relative resource locators
			/// </summary>
			/// <param name="absolutePath">the absolute path</param>
			/// <param name="parent">the ResourceLocation object containing this object</param>
			public ResourceLocationInfoBasePath( Uri absolutePath, ResourceLocation parent )
				: base( parent )
			{
				AbsolutePath = absolutePath;
			}

			#region IEquatable<ResourceLocationInfoId> Members

			/// <summary>
			/// Indicates whether the current object is equal to another object of the same type.
			/// </summary>
			/// <param name="other">An object to compare with this object.</param>
			/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
			public bool Equals( ResourceLocationInfoBasePath other )
			{
				if ( (object) other == null )
				{
					return false;
				}

				return Match( other ) && other.AbsolutePath == AbsolutePath;
			}

			#endregion

			/// <summary>
			/// Determines whether the specified object is equal to the current object.
			/// </summary>
			/// <param name="obj">An object to compare with this object.</param>
			/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
			public override bool Equals( object obj )
			{
				return Equals( obj as ResourceLocationInfoBasePath );
			}

			/// <summary>
			/// Serves as the default hash function.
			/// </summary>
			/// <returns>A hash code for the current object.</returns>
			public override int GetHashCode()
			{
				unchecked
				{
					int hash = 17;

					hash = hash * 23 + AbsolutePath.GetHashCode();

					return hash;
				}
			}

			/// <summary>
			/// Returns whether rl1 equals rl2
			/// </summary>
			/// <param name="rl1"></param>
			/// <param name="rl2"></param>
			/// <returns></returns>
			public static bool operator ==( ResourceLocationInfoBasePath rl1, ResourceLocationInfoBasePath rl2 )
			{
				if ( System.Object.ReferenceEquals( rl1, rl2 ) )
				{
					return true;
				}

				if ( ((object) rl1 == null) || ((object) rl2 == null) )
				{
					return false;
				}

				return rl1.Equals( rl2 );
			}

			/// <summary>
			/// Returns whether rl1 does not equal rl2
			/// </summary>
			/// <param name="rl1"></param>
			/// <param name="rl2"></param>
			/// <returns></returns>
			public static bool operator !=( ResourceLocationInfoBasePath rl1, ResourceLocationInfoBasePath rl2 )
			{
				return !(rl1 == rl2);
			}
		}

		/// <summary>
		/// Resource locator that uses an absolute path Uri for identification.
		/// </summary>
		public class ResourceLocationInfoAbsolutePath : ResourceLocationInfoBase
		{
			/// <summary>
			/// The absolute path.
			/// </summary>
			public Uri AbsolutePath
			{
				get;
				private set;
			}

			/// <summary>
			/// Creates a new resource locator by absolute path Uri
			/// </summary>
			/// <param name="absolutePath">the absolute path</param>
			/// <param name="parent">the ResourceLocation object containing this object</param>
			public ResourceLocationInfoAbsolutePath( Uri absolutePath, ResourceLocation parent )
				: base( parent )
			{
				AbsolutePath = absolutePath;
			}

			#region IEquatable<ResourceLocationInfoId> Members

			/// <summary>
			/// Indicates whether the current object is equal to another object of the same type.
			/// </summary>
			/// <param name="other">An object to compare with this object.</param>
			/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
			public bool Equals( ResourceLocationInfoAbsolutePath other )
			{
				if ( (object) other == null )
				{
					return false;
				}

				return Match( other ) && other.AbsolutePath == AbsolutePath;
			}

			#endregion

			/// <summary>
			/// Determines whether the specified object is equal to the current object.
			/// </summary>
			/// <param name="obj">An object to compare with this object.</param>
			/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
			public override bool Equals( object obj )
			{
				return Equals( obj as ResourceLocationInfoAbsolutePath );
			}

			/// <summary>
			/// Serves as the default hash function.
			/// </summary>
			/// <returns>A hash code for the current object.</returns>
			public override int GetHashCode()
			{
				unchecked
				{
					int hash = 17;

					hash = hash * 23 + AbsolutePath.GetHashCode();

					return hash;
				}
			}

			/// <summary>
			/// Returns whether rl1 equals rl2
			/// </summary>
			/// <param name="rl1"></param>
			/// <param name="rl2"></param>
			/// <returns></returns>
			public static bool operator ==( ResourceLocationInfoAbsolutePath rl1, ResourceLocationInfoAbsolutePath rl2 )
			{
				if ( System.Object.ReferenceEquals( rl1, rl2 ) )
				{
					return true;
				}

				if ( ((object) rl1 == null) || ((object) rl2 == null) )
				{
					return false;
				}

				return rl1.Equals( rl2 );
			}

			/// <summary>
			/// Returns whether rl1 does not equal rl2
			/// </summary>
			/// <param name="rl1"></param>
			/// <param name="rl2"></param>
			/// <returns></returns>
			public static bool operator !=( ResourceLocationInfoAbsolutePath rl1, ResourceLocationInfoAbsolutePath rl2 )
			{
				return !(rl1 == rl2);
			}
		}

		/// <summary>
		/// Resource locator that uses a relative path string for identification.
		/// The path is relative to the parent's LocalBasePath.
		/// </summary>
		public class ResourceLocationInfoLocalPath : ResourceLocationInfoBase
		{
			/// <summary>
			/// The relative path.
			/// It is relative to the parent's LocalBasePath.
			/// </summary>
			public string RelativePath
			{
				get;
				private set;
			}

			/// <summary>
			/// Creates a new resource locator by local path
			/// </summary>
			/// <param name="relativePath">the relative path</param>
			/// <param name="parent">the ResourceLocation object containing this object</param>
			public ResourceLocationInfoLocalPath( string relativePath, ResourceLocation parent )
				: base( parent )
			{
				RelativePath = relativePath;
			}

			/// <summary>
			/// Returns the absolute Uri from this relative Uri or null if it does not exist
			/// </summary>
			/// <returns></returns>
			public Uri GetAbsoluteUri()
			{
				if ( Parent.LocalBasePath != null )
				{
					return new Uri( Parent.LocalBasePath, RelativePath );
				}
				else
				{
					return null;
				}
			}

			#region IEquatable<ResourceLocationInfoId> Members

			/// <summary>
			/// Indicates whether the current object is equal to another object of the same type.
			/// </summary>
			/// <param name="other">An object to compare with this object.</param>
			/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
			public bool Equals( ResourceLocationInfoLocalPath other )
			{
				if ( (object) other == null )
				{
					return false;
				}

				return Match( other ) && other.RelativePath == RelativePath;
			}

			#endregion

			/// <summary>
			/// Determines whether the specified object is equal to the current object.
			/// </summary>
			/// <param name="obj">An object to compare with this object.</param>
			/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
			public override bool Equals( object obj )
			{
				return Equals( obj as ResourceLocationInfoLocalPath );
			}

			/// <summary>
			/// Serves as the default hash function.
			/// </summary>
			/// <returns>A hash code for the current object.</returns>
			public override int GetHashCode()
			{
				unchecked
				{
					int hash = 17;

					hash = hash * 23 + RelativePath.GetHashCode();

					return hash;
				}
			}

			/// <summary>
			/// Returns whether rl1 equals rl2
			/// </summary>
			/// <param name="rl1"></param>
			/// <param name="rl2"></param>
			/// <returns></returns>
			public static bool operator ==( ResourceLocationInfoLocalPath rl1, ResourceLocationInfoLocalPath rl2 )
			{
				if ( System.Object.ReferenceEquals( rl1, rl2 ) )
				{
					return true;
				}

				if ( ((object) rl1 == null) || ((object) rl2 == null) )
				{
					return false;
				}

				return rl1.Equals( rl2 );
			}

			/// <summary>
			/// Returns whether rl1 does not equal rl2
			/// </summary>
			/// <param name="rl1"></param>
			/// <param name="rl2"></param>
			/// <returns></returns>
			public static bool operator !=( ResourceLocationInfoLocalPath rl1, ResourceLocationInfoLocalPath rl2 )
			{
				return !(rl1 == rl2);
			}
		}

		#endregion Inner classes
	}
}
